#ifndef VECTOR_3D
#define VECTOR_3D

#include <math.h>
#include "../define.h"

namespace sleek
{
    namespace core
    {
        namespace math
        {
            template<typename T>
            struct vector3d
            {
                vector3d() { x = y = z = 0; }
                explicit vector3d(T s) { x = y = z = s; }
                vector3d(T w, T h, T l) { x = w; y = h; z = l; }
                vector3d(const vector3d<T>& other) : x(other.x), y(other.y), z(other.z) {}
                ~vector3d() {}

                inline vector3d<T> operator + (vector3d<T> other)
                {
                    vector3d<T> n(x,y,z);
                        n.x += other.x;
                        n.y += other.y;
                        n.z += other.z;
                    return n;
                }
                inline vector3d<T> operator + (T other)
                {
                    vector3d<T> n(x,y,z);
                        n.x += other;
                        n.y += other;
                        n.z += other;
                    return n;
                }
                inline vector3d<T> operator - (vector3d<T> other)
                {
                    vector3d<T> n(x,y,z);
                        n.x -= other.x;
                        n.y -= other.y;
                        n.z -= other.z;
                    return n;
                }
                inline vector3d<T> operator - (T other)
                {
                    vector3d<T> n(x,y,z);
                        n.x -= other;
                        n.y -= other;
                        n.z -= other;
                    return n;
                }
                inline vector3d<T> operator / (vector3d<T> other)
                {
                    vector3d<T> n(x,y,z);
                        n.x /= other.x;
                        n.y /= other.y;
                        n.z /= other.z;
                    return n;
                }
                inline vector3d<T> operator / (T other)
                {
                    vector3d<T> n(x,y,z);
                        n.x /= other;
                        n.y /= other;
                        n.z /= other;
                    return n;
                }
                inline vector3d<T> operator * (vector3d<T> other)
                {
                    vector3d<T> n(x,y,z);
                        n.x *= other.x;
                        n.y *= other.y;
                        n.z *= other.z;
                    return n;
                }
                inline vector3d<T> operator * (T other)
                {
                    vector3d<T> n(x,y,z);
                        n.x *= other;
                        n.y *= other;
                        n.z *= other;
                    return n;
                }
                inline vector3d<T> operator = (vector3d<T> other)
                {
                    x = other.x;
                    y = other.y;
                    z = other.z;
                    return *this;
                }

                inline bool operator < (vector3d<T> other) { return (x < other.x && y < other.y && z < other.z) ? true : false; }
                inline bool operator > (vector3d<T> other) { return (x > other.x && y > other.y && z > other.z) ? true : false; }
                inline bool operator <= (vector3d<T> other) { return (x <= other.x && y <= other.y && z <= other.z) ? true : false; }
                inline bool operator >= (vector3d<T> other) { return (x >= other.x && y >= other.y && z >= other.z) ? true : false; }
                inline bool operator == (vector3d<T> other) { return (x == other.x && y == other.y && z == other.z) ? true : false; }
                inline bool operator != (vector3d<T> other) { return (x != other.x && y != other.y && z != other.z) ? true : false; }

                inline vector3d<T> normalise()
                {
                    vector3d<T> tmp = *this;
                        T n = sqrt(x*x+y*y+z*z);
                        tmp.x /= n; tmp.y /= n; tmp.z /= n;
                    return tmp;
                }
                inline f32 getDistance(vector3d<T> i)
                {
                    return sqrtf((f32) ( ((x-i.x)*(x-i.x)) + ((y-i.y)*(y-i.y)) + ((z-i.z)*(z-i.z)) ));
                }
                inline vector3d<T> crossProduct(const vector3d<T> &a,const vector3d<T> &b)
                {
                    vector3d<T> result;
                        result.x = a.y*b.z - a.z*b.y;
                        result.y = a.z*b.x - a.x*b.z;
                        result.z = a.x*b.y - a.y*b.z;
                    return result;
                }

                inline vector3d<T> operator += (vector3d<T> other){ x += other.x; y += other.y; z += other.z; }
                inline vector3d<T> operator -= (vector3d<T> other){ x -= other.x; y -= other.y; z -= other.z; }
                inline float const *getVertex3fv(){ return &x; }

                T x, y, z;
            };

            typedef vector3d<u8>  vector3dc;
            typedef vector3d<f32> vector3df;
            typedef vector3d<int> vector3di;
            typedef vector3d<u32> vector3du;
            typedef vector3d<s32> vector3ds;

            template<typename T>
            inline vector3df convertToGl(vector3d<T> rec)
            {
                vector3df pos;
                    pos.x = (float)rec.x;
                    pos.y = (float)rec.y;
                    pos.z = (float)rec.z;
                return pos;
            }
        }
    }
}

#endif
